import type { UnwrapRef } from 'vue';
import { isReactive, isRef, ref, watch, unref, reactive } from 'vue';
import type { MaybeRef } from '@/typing';
import { isBoolean, isEqual } from 'lodash-es';
import type { ResponseData } from '@/api/typing';
export interface PageInfo {
    current: number;
    size: number;
    total: number;
    pages: number;
    [key: string]: any;
}

// export interface ResponseData<T> {
//     data: T[];
//     success?: boolean;
//     total?: number;
//     [key: string]: any;
// }

export type RequestParams =
    | {
          size?: number;
          current?: number;
          [key: string]: any;
      }
    | undefined;

export interface UseFetchDataAction<T extends ResponseData<any>> {
    stripe: (record: any, index: number) => string | undefined;
    cancel: () => void;
    reload: () => Promise<void>;
    resetPageIndex: () => void;
    reset: () => void;
    context: Context<T>;
    setPageInfo: (pageInfo: Partial<PageInfo>) => void;
}

export interface Context<T extends ResponseData<any>> {
    current: number;
    pageSize: number;
    dataSource: any[];
    loading: boolean;
    isList: boolean;
    total: number;
    executeImmediately: boolean | undefined;
    requestParams?: MaybeRef<{
        [key: string]: any;
    }>;
    [key: string]: any;
}

export const defaultContext: Context<any> = {
    stripe: false,
    loading: false,
    current: 1,
    pageSize: 20,
    isList: false,
    total: 0,
    executeImmediately: true,
    dataSource: [],
};

const filterNoValidValue = (obj: Record<string, any> = {}) => {
    const newObj = {};
    Object.keys(obj).forEach(k => {
        if (obj[k] !== undefined && obj[k] !== '' && obj[k] !== null) {
            newObj[k] = obj[k];
        }
    });

    return newObj;
};

export const useFetchData = <T extends ResponseData<any>>(
    getData: (params?: RequestParams) => Promise<T>,
    context: MaybeRef<{
        stripe?: boolean;
        current?: number;
        pageSize?: number;
        dataSource?: T['data'];
        loading?: boolean;
        isList?: boolean | undefined;
        executeImmediately: boolean | undefined;
        requestParams?: MaybeRef<{
            [key: string]: any;
        }>;
        [key: string]: any;
    }> = reactive({ ...defaultContext }),
    options?: {
        current?: number;
        pageSize?: number;
        onLoad?: (dataSource: T['data']) => void;
        onRequestError?: (e: Error) => void;
        pagination?: boolean;
    },
): UseFetchDataAction<T> => {
    const state = reactive({ ...defaultContext } as Context<T>);
    const mergeContext: any = isReactive(context) || isRef(context) ? context : ref(context);
    watch(
        mergeContext,
        () => {
            Object.assign(state, unref(context));
        },
        { immediate: true },
    );

    const fetchList = async () => {
        // 请求中禁止重复请求
        // if (state.loading) {
        //     return;
        // }
        state.loading = true;
        const { pageSize = 20, current = 1 } = state;
        try {
            const params: RequestParams =
                options?.pagination !== false
                    ? {
                          current,
                          size: pageSize,
                          ...filterNoValidValue(unref(mergeContext).requestParams),
                      }
                    : filterNoValidValue(unref(mergeContext).requestParams);
            if (isBoolean(mergeContext.isList) && mergeContext.isList) {
                const { data } = await ((mergeContext.queryMethod as any) ? mergeContext.queryMethod : getData)(params);

                state.loading = false;
                state.dataSource = data as any[];
                state.total = data.length;
            } else {
                const {
                    data: { records, total: dataTotal = 0 },
                } = await ((mergeContext.queryMethod as any) ? mergeContext.queryMethod : getData)(params);

                state.loading = false;
                state.dataSource = records as any[];
                state.total = dataTotal;
            }
        } catch (e: any) {
            state.loading = false;
            // 如果没有传递这个方法的话，需要把错误抛出去，以免吞掉错误
            if (options?.onRequestError === undefined) {
                throw new Error(e);
            } else {
                options.onRequestError(e);
            }
        }
    };

    const cancel = () => {};

    const reset = () => {};

    const reload = (): Promise<void> => {
        return new Promise(resolve => {
            resolve(fetchList());
        });
    };

    const setPageInfo = (pageInfo: Partial<PageInfo>) => {
        console.warn('setPageInfo 废弃，请直接使用响应式 context');
        pageInfo.current && (state.current = pageInfo.current);
        pageInfo.pageSize && (state.pageSize = pageInfo.pageSize);
    };

    const resetPageIndex = (): void => {
        console.warn('resetPageIndex 废弃，请直接使用响应式 context');
        // state.current = 1;
    };
    watch(
        [() => state.current, () => state.pageSize, () => ({ ...state.requestParams }), () => state.executeImmediately],
        (nextValue, preValue) => {
            // console.log(nextValue, preValue, state.executeImmediately);
            if (!isEqual(nextValue, preValue) && state.executeImmediately) {
                fetchList().catch(e => {
                    throw new Error(e);
                });
            }
        },
        { immediate: true, deep: true },
    );

    const stripe = (_: any, index: number) => index % 2 === 1 && state.stripe && 'ant-pro-table-row-striped';

    return {
        stripe,
        cancel,
        reset,
        reload,
        resetPageIndex,
        setPageInfo: info => {
            setPageInfo({
                current: state.current,
                pageSize: state.pageSize,
                ...info,
            });
        },
        context: state,
    };
};
